import base64
import datetime
from google.appengine.ext import db
from google.appengine.api import memcache

class Tag(db.Model):
  name = db.StringProperty()
  refCount = db.IntegerProperty(default = 0)
  
class Record(db.Model):
  date = db.DateTimeProperty()
  dfmt = db.StringProperty()
  value = db.FloatProperty()
  unit = db.StringProperty()
  tags = db.StringListProperty()
  source = db.StringProperty()
  inputtime = db.DateTimeProperty(default = datetime.datetime.now())

class History(db.Model):
  tags = db.StringListProperty()
  qcount = db.IntegerProperty(default = 1)
  found = db.BooleanProperty(default = True)

def GenRecordKey(date, tags):
  tags.sort()
  return 'r'+base64.b64encode(" ".join(tags).encode("utf-8")+date.strftime("%Y%m%d%H%M%S"))

def GenTagKey(tag):
  return 't'+base64.b64encode(tag.encode("utf-8"))

def GenHistoryKey(tags):
  return 'h'+base64.b64encode(" ".join(tags).encode("utf-8"))

def GetRecord(date, tags):
  item = memcache.get(GenRecordKey(date, tags))
  if item is not None:
    return item
  query = Record.gql("WHERE date = :1 and tags = :2", date, tags)
  if query.count() > 0:
    memcache.set(GenRecordKey(date, tags), (True, query[0]))
    return (True, query[0])
  else:
    memcache.set(GenRecordKey(date, tags), (False, None))
  return (False, None)

def GetTag(tag):
  item = memcache.get(GenTagKey(tag))
  if item is not None:
    return item
  query = Tags.gql("WHERE name = :1", tag)
  if query.count() > 0:
    memcache.set(GenTagKey(tag), (True, query[0]))
    return (True, query[0])
  else:
    memcache.set(GenTagKey(tag), (False, None))
  return (False, None)

def GetHistory(tags):
  item = memcache.get(GenHistoryKey(tags))
  if item is not None:
    return item
  query = History.gql("WHERE tags = :1", tags)
  if query.count() > 0:
    memcache.set(GenHistoryKey(tags), (True, query[0]))
    return (True, query[0])
  else:
    memcache.set(GenHistoryKey(tags), (False, None))
  return (False, None)

def UpdateEntity(entities, exist):
  e = dict((k, (exist, v)) for k, v in entities.iteritems()) 
  memcache.set_multi(e)

def DBPut(entities, batch=100):
  start = 0
  while len(entities[start:batch+start]) > 0:
    db.put(entities[start:batch+start])
    start += batch

def GetRecords(date, begin, end, tags, bookmark, pagesize=100):
  """
  http://google-appengine.googlegroups.com/web/efficient_paging_using_key_instead_of_a_dedicated_unique_property.txt
  *Original starting query* 
  WHERE x > 0 AND x < 9
  *Bookmarkable starting query*
  WHERE x > 0 AND x < 9 ORDER BY x ASC, __key__ ASC
  *Derived queries for starting from bookmark entity B*
  WHERE x = B.x AND __key__ > B ORDER BY __key__ ASC
  WHERE x > B.x AND x < 9 ORDER BY x ASC, __key__ ASC 
  """

  if 0 == len(tags):
    gqls = ["WHERE "+date+">=:1 AND "+date+"<=:2 ORDER BY "+date+" ASC, __key__ ASC",
            "WHERE "+date+"=:1 AND __key__>=:2 ORDER BY __key__ ASC",
            "WHERE "+date+">:1 AND "+date+"<=:2 ORDER BY "+date+" ASC, __key__ ASC"
            ]
  else:
    gqls = ["WHERE "+date+">=:1 AND "+date+"<=:2 AND tags=:3 ORDER BY "+date+" ASC, __key__ ASC",
            "WHERE "+date+"=:1 AND __key__>=:2 AND tags=:3  ORDER BY __key__ ASC",
            "WHERE "+date+">:1 AND "+date+"<=:2 AND tags=:3 ORDER BY "+date+" ASC, __key__ ASC"
            ]
  if bookmark is None:
    if 0 == len(tags):
      query = Record.gql(gqls[0], begin, end)
    else:
      query = Record.gql(gqls[0], begin, end, tags)
    records = query.fetch(pagesize+1)
  else:
    bookmarkv = {"inputtime": bookmark.inputtime,
                 "date": bookmark.date}
    if 0 == len(tags):
      query1 = Record.gql(gqls[1], bookmarkv[date], bookmark.key())
      query2 = Record.gql(gqls[2], bookmarkv[date], end)
    else:
      query1 = Record.gql(gqls[1], bookmarkv[date], bookmark.key(), tags)
      query2 = Record.gql(gqls[2], bookmarkv[date], end, tags)
    records = query1.fetch(pagesize+1)
    records += query2.fetch(pagesize+1-len(records))
  if len(records)<pagesize+1:
    return records, None
  else:
    return records[:pagesize], records[-1]
